#ifndef __VirtualMachine__
#define __VirtualMachine__

#include "neiderra/core/Types.h"
#include "Stack.h"
#include "neiderra/utils/Logger.h"

#define VM_REGISTERS_NUM	16
#define VM_POINTER_SIZE		4

using namespace neiderra::types;
using namespace neiderra::core;

namespace neiderra { 
	namespace vm {

		//! Virtual machine class 
		class VirtualMachine: public Base
		{
		public:	
			struct exBadOperation{};

			enum Command {
				cmdPUSH,	// offset, size
				cmdPUSHR,	// r[addr], size
				cmdPOP,		// offset, size
				cmdPOPR,	// r[addr], size
				cmdTOP,
				cmdTOPR,
				cmdTOPM,
				cmdADD,		// r[addr1], r[addr2], size
				cmdSUB,		// r[addr1], r[addr2], size
				cmdDIV,		// --"--"--
				cmdMUL,
				cmdDEC,		// offset, size
				cmdNOP,		// 
				cmdNEW,		// offset, size
				cmdCALL,	// 
				cmdMOV,		// move to memory addr
//				cmdMOVR,	// move to register
				cmdLOG,
				cmdLD,
				cmdINC,
				cmdCP,
				cmdDATA,	// [data N] - add N to stack pointer (reserve dataspace)
				cmdBREQ,
				cmdBRNEQ,
				cmdBRGE,
				cmdRET,
				cmdGOTO,
				cmdST,		// [st x, r] x - address, r - register
				cmdORG,
				cmdDBM,
				cmdSTM,
				cmdPUSHM,
				cmdPOPM
			};

			enum {
				flagZero
			};

			static const unsigned CommandNum=31;

			VirtualMachine(Core* core, unsigned heapSize, unsigned stackSize, unsigned retStackSize);
			virtual ~VirtualMachine();

			int run();
			void setProgram(nByte* bytecode, unsigned size);
			int execute();
			
		private:
            int getInt(nByte*);
			float getFloat(nByte*);
			void readBytes(nByte*, unsigned);
			Command readCommand();
			void pushRegisters();
			void popRegisters();
			void pushFlags();
			void popFlags();
			void resetFlags();
			void pushState();
			void popState();
			
			void logState();

		private:
			unsigned ptr_act;
			unsigned _pc;
			unsigned _programSize;
//			nByte addr1, addr2, val;
			nByte* _bytecode;
			nByte* _heap;
			Stack* _stack;
			Stack* _retstack;
			Stack* _regstack;
			Stack* _flagstack;
			bool _stop;
			int* r;
			bool* flags;
//			Logger *_log;
		};

	}
}

#endif 
